home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / gnulib / rcs4 / source / rcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-07  |  55.8 KB  |  1,674 lines

  1. /*
  2.  *                      RCS create/change operation
  3.  */
  4. #ifndef lint
  5. static char rcsid[]=
  6. "$Header: d:/rcs/rcs/rcs.c 5.5 91/02/07 14:24:36 ROOT_DOS Exp $ Purdue CS";
  7. #endif
  8. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  9.    Distributed under license by the Free Software Foundation, Inc.
  10.  
  11. This file is part of RCS.
  12.  
  13. RCS is free software; you can redistribute it and/or modify
  14. it under the terms of the GNU General Public License as published by
  15. the Free Software Foundation; either version 1, or (at your option)
  16. any later version.
  17.  
  18. RCS is distributed in the hope that it will be useful,
  19. but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. GNU General Public License for more details.
  22.  
  23. You should have received a copy of the GNU General Public License
  24. along with RCS; see the file COPYING.  If not, write to
  25. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  26.  
  27. Report problems and direct all questions to:
  28.  
  29.     rcs-bugs@cs.purdue.edu
  30.  
  31. */
  32.  
  33.  
  34.  
  35.  
  36. /* $Log:    rcs.c $
  37.  * Revision 5.5  91/02/07  14:24:36  ROOT_DOS
  38.  * The output file was STILL being created read-only !
  39.  * 
  40.  * Revision 5.4  91/02/07  13:51:58  ROOT_DOS
  41.  * Make temporary files writeable (were RDONLY !)
  42.  * 
  43.  * Revision 5.3  90/07/15  20:24:19  lfk
  44.  * checked in with -k by ROOT_DOS at 91.02.07.11.44.53.
  45.  * 
  46.  * Revision 5.3  90/07/15  20:24:19  lfk
  47.  * Most major fixes added between rev 5.1 and rev 5.5:
  48.  *     signals fixed so they work on MS-DOS
  49.  *     Added MKS arguments code so argv can be large
  50.  *     added code to handle slashes a'la Unix
  51.  *     added more file extensions to system from MS-DOS
  52.  * 
  53.  * Revision 5.2  90/07/15  11:32:15  ROOT_DOS
  54.  * DOS version of RCS 4.0 checked in for MODS
  55.  * by lfk@athena.mit.edu
  56.  * Also update to MSC 6.0
  57.  * 
  58.  * Revision 4.11  89/05/01  15:12:06  narten
  59.  * changed copyright header to reflect current distribution rules
  60.  * 
  61.  * Revision 4.10  88/11/08  16:01:54  narten
  62.  * didn't install previous patch correctly
  63.  * 
  64.  * Revision 4.9  88/11/08  13:56:01  narten
  65.  * removed include <sysexits.h> (not needed)
  66.  * minor fix for -A option
  67.  * 
  68.  * Revision 4.8  88/11/08  12:01:58  narten
  69.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  70.  * 
  71.  * Revision 4.8  88/08/09  19:12:27  eggert
  72.  * Don't access freed storage.
  73.  * Use execv(), not system(); yield proper exit status; remove lint.
  74.  * 
  75.  * Revision 4.7  87/12/18  11:37:17  narten
  76.  * lint cleanups (Guy Harris)
  77.  * 
  78.  * Revision 4.6  87/10/18  10:28:48  narten
  79.  * Updating verison numbers. Changes relative to 1.1 are actually 
  80.  * relative to 4.3
  81.  * 
  82.  * Revision 1.4  87/09/24  13:58:52  narten
  83.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  84.  * warnings)
  85.  * 
  86.  * Revision 1.3  87/03/27  14:21:55  jenkins
  87.  * Port to suns
  88.  * 
  89.  * Revision 1.2  85/12/17  13:59:09  albitz
  90.  * Changed setstate to rcs_setstate because of conflict with random.o.
  91.  * 
  92.  * Revision 1.1  84/01/23  14:50:09  kcs
  93.  * Initial revision
  94.  * 
  95.  * Revision 4.3  83/12/15  12:27:33  wft
  96.  * rcs -u now breaks most recent lock if it can't find a lock by the caller.
  97.  * 
  98.  * Revision 4.2  83/12/05  10:18:20  wft
  99.  * Added conditional compilation for sending mail.
  100.  * Alternatives: V4_2BSD, V6, USG, and other.
  101.  * 
  102.  * Revision 4.1  83/05/10  16:43:02  wft
  103.  * Simplified breaklock(); added calls to findlock() and getcaller().
  104.  * Added option -b (default branch). Updated -s and -w for -b.
  105.  * Removed calls to stat(); now done by pairfilenames().
  106.  * Replaced most catchints() calls with restoreints().
  107.  * Removed check for exit status of delivermail().
  108.  * Directed all interactive output to stderr.
  109.  * 
  110.  * Revision 3.9.1.1  83/12/02  22:08:51  wft
  111.  * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
  112.  * 
  113.  * Revision 3.9  83/02/15  15:38:39  wft
  114.  * Added call to fastcopy() to copy remainder of RCS file.
  115.  *
  116.  * Revision 3.8  83/01/18  17:37:51  wft
  117.  * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
  118.  *
  119.  * Revision 3.7  83/01/15  18:04:25  wft
  120.  * Removed putree(); replaced with puttree() in rcssyn.c.
  121.  * Combined putdellog() and scanlogtext(); deleted putdellog().
  122.  * Cleaned up diagnostics and error messages. Fixed problem with
  123.  * mutilated files in case of deletions in 2 files in a single command.
  124.  * Changed marking of selector from 'D' to DELETE.
  125.  *
  126.  * Revision 3.6  83/01/14  15:37:31  wft
  127.  * Added ignoring of interrupts while new RCS file is renamed;
  128.  * Avoids deletion of RCS files by interrupts.
  129.  *
  130.  * Revision 3.5  82/12/10  21:11:39  wft
  131.  * Removed unused variables, fixed checking of return code from diff,
  132.  * introduced variant COMPAT2 for skipping Suffix on -A files.
  133.  *
  134.  * Revision 3.4  82/12/04  13:18:20  wft
  135.  * Replaced getdelta() with gettree(), changed breaklock to update
  136.  * field lockedby, added some diagnostics.
  137.  *
  138.  * Revision 3.3  82/12/03  17:08:04  wft
  139.  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
  140.  * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
  141.  * fixed -u for missing revno. Disambiguated structure members.
  142.  *
  143.  * Revision 3.2  82/10/18  21:05:07  wft
  144.  * rcs -i now generates a file mode given by the umask minus write permission;
  145.  * otherwise, rcs keeps the mode, but removes write permission.
  146.  * I added a check for write error, fixed call to getlogin(), replaced
  147.  * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
  148.  * conflicting, long identifiers.
  149.  *
  150.  * Revision 3.1  82/10/13  16:11:07  wft
  151.  * fixed type of variables receiving from getc() (char -> int).
  152.  */
  153.  
  154.  
  155. #include <sys/types.h>
  156. #include <sys/stat.h>
  157. #include "rcsbase.h"
  158. #ifndef lint
  159. static char rcsbaseid[] = RCSBASE;
  160. #endif
  161.  
  162. extern FILE * fopen();
  163. extern char * bindex();
  164. extern int  expandsym();                /* get numeric revision name        */
  165. extern struct  hshentry  * getnum();
  166. extern struct  lock      * addlock();   /* add a lock                       */
  167. extern char              * getid();
  168. extern char              Klog[], Khead[], Kaccess[], Kbranch[], Ktext[];
  169. #ifdef COMPAT2
  170. extern char Ksuffix[];
  171. #endif
  172. extern char * getcaller();              /* get login of caller              */
  173. extern struct hshentry   * findlock();  /* find and remove lock             */
  174. extern struct hshentry   * genrevs();
  175. extern char * checkid();                /* check an identifier              */
  176. extern char * getfullRCSname();         /* get full path name of RCS file   */
  177. extern char * mktempfile();             /* temporary file name generator    */
  178. extern free();
  179. extern void catchints();
  180. extern void ignoreints();
  181. extern int  nerror;                     /* counter for errors               */
  182. extern int  quietflag;                  /* diagnoses suppressed if true     */
  183. extern char curlogmsg[];                /* current log message              */
  184. extern char * resultfile;               /* filename for fcopy            */
  185. extern FILE *fcopy;                     /* result file during editing       */
  186. extern FILE * finptr;                   /* RCS input file                   */
  187. extern FILE * frewrite;                 /* new RCS file                     */
  188. extern int    rewriteflag;              /* indicates whether input should be*/
  189.                     /* echoed to frewrite               */
  190.  
  191. char * newRCSfilename, * diffilename, * cutfilename;
  192. char * RCSfilename, * workfilename;
  193. extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
  194. extern int  haveRCSstat, haveworkstat;/* status indicators                  */
  195.  
  196. char accessorlst[strtsize];
  197. FILE * fcut;        /* temporary file to rebuild delta tree                 */
  198. int    oldumask;    /* save umask                                           */
  199.  
  200. int initflag, strictlock, strict_selected, textflag;
  201. char * textfile, * accessfile;
  202. char * caller, numrev[30];            /* caller's login;               */
  203. struct  access  * newaccessor,  * rmvaccessor,  * rplaccessor;
  204. struct  access  *curaccess,  *rmaccess;
  205. struct  hshentry        * gendeltas[hshsize];
  206.  
  207. #ifdef MSDOS
  208. extern char *gettmpdir();
  209. char    tmpdir[NCPPN];
  210. #endif /* MSDOS */
  211.  
  212. struct  Lockrev {
  213.         char    * revno;
  214.         struct  Lockrev   * nextrev;
  215. };
  216.  
  217. struct  Symrev {
  218.         char    * revno;
  219.         char    * ssymbol;
  220.         int     override;
  221.         struct  Symrev  * nextsym;
  222. };
  223.  
  224. struct  Status {
  225.         char    * revno;
  226.         char    * status;
  227.         struct  Status  * nextstatus;
  228. };
  229.  
  230. struct delrevpair {
  231.         char    * strt;
  232.         char    * end;
  233.         int     code;
  234. };
  235.  
  236. struct  Lockrev * newlocklst,   * rmvlocklst;
  237. struct  Symrev  * assoclst,  * lastassoc;
  238. struct  Status  * statelst,  * laststate;
  239. struct  delrevpair      * delrev;
  240. struct  hshentry        * cuthead,  *cuttail,  * delstrt;
  241. char    branchnum[revlength], * branchsym;
  242. struct  hshentry branchdummy;
  243. char    * commsyml;
  244. char    * headstate;
  245. int     lockhead,unlockcaller,chgheadstate,branchflag,commentflag;
  246. int     delaccessflag;
  247. enum    stringwork {copy, edit, empty}; /* expand and edit_expand not needed */
  248.  
  249.  
  250. #ifdef MKS
  251. main(int argc, char *argv[], char *env[])
  252. #else
  253. main (argc, argv)
  254. int argc;
  255. char * argv[];
  256. #endif /* MKS */
  257. {
  258.         char    *comdusge;
  259.         int     result;
  260.     struct    access    *removeaccess(),  * getaccessor();
  261.         struct  Lockrev *rmnewlocklst();
  262.         struct  Lockrev *curlock,  * rmvlock, *lockpt;
  263.         struct  Status  * curstate;
  264.         struct  access    *temp, *temptr;
  265.     int status;
  266. #ifdef MKS
  267.     int z = 0;
  268.     int ARGC = 0;
  269.     char **tmp;
  270.     char *env_name;
  271. #    define MAXARGS 500        /* This means 500 items on the command line */
  272.     if (!(tmp = (char **)malloc(sizeof(char *)*(MAXARGS+1)))) {
  273.         fprintf(stderr, "%s: can't allocate space for arguments\n",argv[0]);
  274.         exit(1);
  275.     }
  276.     for ( z = 0 ; env[z] != NULL; z++) {    /* loop through environment */
  277.         if (*env[z] == '~') {    /* testing for entries begining with '~' */
  278.             *++env[z];            /* increment pointer to delete '~' */
  279.             tmp[ARGC++] = env[z];    /* add it to our new array */
  280.             if (z >= MAXARGS) {
  281.                 fprintf(stderr, "%s: can't handle any more arguments\n", argv[0]);
  282.                 goto list;
  283.             }
  284.         }
  285.         else if (*env[z] == '_') {    /* testing for entries begining with _ */
  286.             *++env[z] ; *++env[z];    /* move past the '_' and the '=' */
  287.             env_name = env[z];    /* copy the name */
  288.         }
  289.     }
  290. list:
  291.     if ( STREQ( (char *) argv[0] , env_name ) ) {    /* test name against startup args */
  292.         /* environment arguments meant for this program */
  293. #    ifdef DEBUG
  294.         printf("Using shell supplied args\n");
  295. #    endif
  296.         argc = ARGC;
  297.         tmp[ARGC] = NULL;    /* the terminal NULL */
  298.         argv = tmp;
  299.     }
  300. #    ifdef DEBUG
  301.     else 
  302.         printf("Using startup supplied args\n");
  303. #    endif /* debug */
  304. #endif /* MKS */
  305.  
  306.     status = 0;
  307.         nerror = 0;
  308.     catchints();
  309.         cmdid = "rcs";
  310.         quietflag = false;
  311.         comdusge ="command format:\nrcs -i -alogins -Alogins -e[logins] -b[rev] -c[commentleader] -l[rev] -u[rev] -L -U -nname[:rev] -Nname[:rev] -orange -sstate[:rev] -t[textfile] file....";
  312.         rplaccessor = nil;     delstrt = nil;
  313.         accessfile = textfile = caller = nil;
  314.         branchflag = commentflag = chgheadstate = false;
  315.         lockhead = false; unlockcaller=false;
  316.         initflag= textflag = false;
  317.         strict_selected = 0;
  318.  
  319.     caller=getcaller();
  320.         laststate = statelst = nil;
  321.         lastassoc = assoclst = nil;
  322.         curlock = rmvlock = newlocklst = rmvlocklst = nil;
  323.         curaccess = rmaccess = rmvaccessor = newaccessor = nil;
  324.         delaccessflag = false;
  325.  
  326.         /*  preprocessing command options    */
  327.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  328.                 switch ((*argv)[1]) {
  329.  
  330.                 case 'i':   /*  initail version  */
  331.                         initflag = true;
  332.                         break;
  333.  
  334.                 case 'b':  /* change default branch */
  335.                         if (branchflag)warn("Redfinition of option -b");
  336.                         branchflag= true;
  337.                         branchsym = (*argv)+2;
  338.                         break;
  339.  
  340.                 case 'c':   /*  change comment symbol   */
  341.                         if (commentflag)warn("Redefinition of option -c");
  342.                         commentflag = true;
  343.                         commsyml = (*argv)+2;
  344.                         break;
  345.  
  346.                 case 'a':  /*  add new accessor   */
  347.                         if ( (*argv)[2] == '\0') {
  348.                             error("Login name missing after -a");
  349.                         }
  350.                         if ( (temp = getaccessor((*argv)+1)) ) {
  351.                             if ( newaccessor )
  352.                                 curaccess->nextaccess = temp->nextaccess;
  353.                             else
  354.                                 newaccessor = temp->nextaccess;
  355.                             temp->nextaccess = nil;
  356.                             curaccess = temp;
  357.                         }
  358.                         break;
  359.  
  360.                 case 'A':  /*  append access list according to accessfile  */
  361.                         if ( (*argv)[2] == '\0') {
  362.                             error("Missing file name after -A");
  363.                             break;
  364.                         }
  365.                         if ( accessfile) warn("Redefinition of option -A");
  366.                         *argv = *argv+2;
  367.                         if( pairfilenames(1, argv, true, false) > 0) {
  368.                             releaselst(newaccessor);
  369.                             newaccessor = curaccess = nil;
  370.                             releaselst(rmvaccessor);
  371.                             rmvaccessor = rmaccess = nil;
  372.                             accessfile = RCSfilename;
  373.                         }
  374.                         else
  375.                             accessfile = nil;
  376.                         break;
  377.  
  378.                 case 'e':    /*  remove accessors   */
  379.                         if ( (*argv)[2] == '\0' ) {
  380.                             delaccessflag = true;
  381.                             break;
  382.                         }
  383.                         if ( (temp = getaccessor((*argv)+1))  ) {
  384.                             if ( rmvaccessor )
  385.                                 rmaccess->nextaccess = temp->nextaccess;
  386.                             else
  387.                                 rmvaccessor = temp->nextaccess;
  388.                             temptr = temp->nextaccess;
  389.                             temp->nextaccess = nil;
  390.                             rmaccess = temp;
  391.                             while( temptr ) {
  392.                                 newaccessor = removeaccess(temptr,newaccessor,false);
  393.                                 temptr = temptr->nextaccess;
  394.                             }
  395.                             curaccess = temp = newaccessor;
  396.                             while( temp){
  397.                                 curaccess = temp;
  398.                                 temp = temp->nextaccess;
  399.                             }
  400.                         }
  401.                         break;
  402.  
  403.                 case 'l':    /*   lock a revision if it is unlocked   */
  404.                         if ( (*argv)[2] == '\0'){ /* lock head or def. branch */
  405.                             lockhead = true;
  406.                             break;
  407.                         }
  408.                         lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
  409.                         lockpt->revno = (*argv)+2;
  410.                         lockpt->nextrev = nil;
  411.                         if ( curlock )
  412.                             curlock->nextrev = lockpt;
  413.                         else
  414.                             newlocklst = lockpt;
  415.                         curlock = lockpt;
  416.                         break;
  417.  
  418.                 case 'u':   /*  release lock of a locked revision   */
  419.                         if ( (*argv)[2] == '\0'){ /*  unlock head  */
  420.                             unlockcaller=true;
  421.                             break;
  422.                         }
  423.                         lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
  424.                         lockpt->revno = (*argv)+2;
  425.                         lockpt->nextrev = nil;
  426.                         if (rmvlock)
  427.                             rmvlock->nextrev = lockpt;
  428.                         else
  429.                             rmvlocklst = lockpt;
  430.                         rmvlock = lockpt;
  431.  
  432.                         curlock = rmnewlocklst(lockpt);
  433.                         break;
  434.  
  435.                 case 'L':   /*  set strict locking */
  436.                         if (strict_selected++) {  /* Already selected L or U? */
  437.                if (!strictlock)      /* Already selected -U? */
  438.                    warn("Option -L overrides -U");
  439.                         }
  440.                         strictlock = true;
  441.                         break;
  442.  
  443.                 case 'U':   /*  release strict locking */
  444.                         if (strict_selected++) {  /* Already selected L or U? */
  445.                if (strictlock)      /* Already selected -L? */
  446.                    warn("Option -L overrides -U");
  447.                         }
  448.             else
  449.                 strictlock = false;
  450.                         break;
  451.  
  452.                 case 'n':    /*  add new association: error, if name exists */
  453.                         if ( (*argv)[2] == '\0') {
  454.                             error("Missing symbolic name after -n");
  455.                             break;
  456.                         }
  457.                         getassoclst(false, (*argv)+1);
  458.                         break;
  459.  
  460.                 case 'N':   /*  add or change association   */
  461.                         if ( (*argv)[2] == '\0') {
  462.                             error("Missing symbolic name after -N");
  463.                             break;
  464.                         }
  465.                         getassoclst(true, (*argv)+1);
  466.                         break;
  467.  
  468.                 case 'o':   /*  delete revisins  */
  469.                         if (delrev) warn("Redefinition of option -o");
  470.                         if ( (*argv)[2] == '\0' ) {
  471.                             error("Missing revision range after -o");
  472.                             break;
  473.                         }
  474.                         getdelrev( (*argv)+1 );
  475.                         break;
  476.  
  477.                 case 's':   /*  change state attribute of a revision  */
  478.                         if ( (*argv)[2] == '\0') {
  479.                             error("State missing after -s");
  480.                             break;
  481.                         }
  482.                         getstates( (*argv)+1);
  483.                         break;
  484.  
  485.                 case 't':   /*  change descriptive text   */
  486.                         textflag=true;
  487.                         if ((*argv)[2]!='\0'){
  488.                                 if (textfile!=nil)warn("Redefinition of -t option");
  489.                                 textfile = (*argv)+2;
  490.                         }
  491.                         break;
  492.  
  493.                 case 'q':
  494.                         quietflag = true;
  495.                         break;
  496.                 default:
  497.                         faterror("Unknown option: %s\n%s", *argv, comdusge);
  498.                 };
  499.         }  /* end processing of options */
  500.  
  501.         if (argc<1) faterror("No input file\n%s", comdusge);
  502.         if (nerror) {   /*  exit, if any error in command options  */
  503.             diagnose("%s aborted",cmdid);
  504.             exit(1);
  505.         }
  506.         if (accessfile) /*  get replacement for access list   */
  507.             getrplaccess();
  508.         if (nerror) {
  509.             diagnose("%s aborted",cmdid);
  510.             exit(1);
  511.         }
  512. #ifdef MSDOS
  513.     strcpy( tmpdir, gettmpdir() );
  514. #endif /* MSDOS */        
  515.     /* now handle all filenames */
  516.         do {
  517.         rewriteflag = false;
  518.         finptr=frewrite=NULL;
  519.  
  520.         if ( initflag ) {
  521.             switch( pairfilenames(argc, argv, false, false) ) {
  522.                 case -1: break;        /*  not exist; ok */
  523.                 case  0: continue;     /*  error         */
  524.                 case  1: error("file %s exists already", RCSfilename);
  525.                          VOID fclose(finptr);
  526.                          continue;
  527.             }
  528.     }
  529.         else  {
  530.             switch( pairfilenames(argc, argv, true, false) ) {
  531.                 case -1: continue;    /*  not exist      */
  532.                 case  0: continue;    /*  errors         */
  533.                 case  1: break;       /*  file exists; ok*/
  534.             }
  535.     }
  536.  
  537.  
  538.         /* now RCSfilename contains the name of the RCS file, and
  539.          * workfilename contains the name of the working file.
  540.          * if !initflag, finptr contains the file descriptor for the
  541.          * RCS file. The admin node is initialized.
  542.          */
  543.  
  544.         diagnose("RCS file: %s", RCSfilename);
  545.  
  546.         if (!trydiraccess(RCSfilename))            continue; /* give up */
  547.         if (!initflag && !checkaccesslist(caller)) continue; /* give up */
  548.         if (!trysema(RCSfilename,true))            continue; /* give up */
  549.  
  550.         gettree(); /* read in delta tree */
  551.  
  552.         /*  update admin. node    */
  553.         if (strict_selected) StrictLocks = strictlock;
  554.         if (commentflag) Comment = commsyml;
  555.  
  556.         /* update default branch */
  557.         if (branchflag && expandsym(branchsym, branchnum)) {
  558.             if (countnumflds(branchnum)>0) {
  559.                 branchdummy.num=branchnum;
  560.                 Dbranch = &branchdummy;
  561.             } else
  562.                 Dbranch = nil;
  563.         }
  564.  
  565.         /*  update access list   */
  566.         if ( delaccessflag ) AccessList = nil;
  567.         if ( accessfile ) {
  568.             temp = rplaccessor;
  569.             while( temp ) {
  570.                 temptr = temp->nextaccess;
  571.                 if ( addnewaccess(temp) )
  572.                     temp->nextaccess = nil;
  573.                 temp = temptr;
  574.             }
  575.         }
  576.         temp = rmvaccessor;
  577.         while(temp)   {         /*  remove accessors from accesslist   */
  578.             AccessList = removeaccess(temp, AccessList,true);
  579.             temp = temp->nextaccess;
  580.         }
  581.         temp = newaccessor;
  582.         while( temp)  {         /*  add new accessors   */
  583.             temptr = temp->nextaccess;
  584.             if ( addnewaccess( temp ) )
  585.                 temp->nextaccess = nil;
  586.             temp = temptr;
  587.         }
  588.  
  589.         updateassoc();          /*  update association list   */
  590.  
  591.         updatelocks();          /*  update locks              */
  592.  
  593.         /*  update state attribution  */
  594.         if (chgheadstate) {
  595.             /* change state of default branch or head */
  596.             if (Dbranch==nil) {
  597.                 if (Head==nil)
  598.                      warn("Can't change states in an empty tree");
  599.                 else Head->state = headstate;
  600.             } else {
  601.                 rcs_setstate(Dbranch->num,headstate); /* Can't set directly */
  602.             }
  603.         }
  604.         curstate = statelst;
  605.         while( curstate ) {
  606.             rcs_setstate(curstate->revno,curstate->status);
  607.             curstate = curstate->nextstatus;
  608.         }
  609.  
  610.         cuthead = cuttail = nil;
  611.         if ( delrev && removerevs()) {
  612.             /*  rebuild delta tree if some deltas are deleted   */
  613.             if ( cuttail )
  614.         VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
  615.                  (char *)nil, gendeltas);
  616.             buildtree();
  617.         }
  618.  
  619.  
  620.         /* prepare for rewriting the RCS file */
  621.         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  622. #ifndef MSDOS
  623.         oldumask = umask(0222); /* turn off write bits */
  624. #endif /* MSDOS */
  625.         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
  626.                 VOID fclose(finptr);
  627.                 error("Can't open file %s",newRCSfilename);
  628.                 continue;
  629.         }
  630. #ifndef MSDOS
  631.         VOID umask(oldumask);
  632. #endif
  633.         putadmin(frewrite);
  634.         if ( Head )
  635.            puttree(Head, frewrite);
  636.     putdesc(initflag,textflag,textfile,quietflag);
  637.         rewriteflag = false;
  638.  
  639.         if ( Head) {
  640.             if (!delrev) {
  641.                 /* no revision deleted */
  642.                 fastcopy(finptr,frewrite);
  643.             } else {
  644.                 if ( cuttail )
  645.                     buildeltatext(gendeltas);
  646.                 else
  647.                     scanlogtext((struct hshentry *)nil,empty);
  648.                     /* copy rest of delta text nodes that are not deleted      */
  649.             }
  650.         }
  651.         ffclose(frewrite);   frewrite = NULL;
  652.         if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
  653.         ignoreints();        /* ignore interrupts */
  654. #ifdef MSDOS
  655.             if(Rename(newRCSfilename,RCSfilename)<0) {
  656. #else
  657.             if(rename(newRCSfilename,RCSfilename)<0) {
  658. #endif /* MSDOS */
  659.                 error("Can't create RCS file %s; saved in %s",
  660.                    RCSfilename, newRCSfilename);
  661.                 newRCSfilename[0] = '\0';  /*  avoid deletion by cleanup  */
  662.                 restoreints();
  663.                 VOID cleanup();
  664.                 break;
  665.             }
  666.             newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
  667.             /* update mode */
  668.             result=0;
  669.             if (!initflag) /* preserve mode bits */
  670.                 result=chmod(RCSfilename,RCSstat.st_mode & ~0222);
  671.             elsif (haveworkstat==0)  /* initialization, and work file exists */
  672.                 result=chmod(RCSfilename,workstat.st_mode & ~0222);
  673.             if (result<0) warn("Can't set mode of %s",RCSfilename);
  674.  
  675.             restoreints();                /* catch them all again */
  676.             diagnose("done");
  677.         } else {
  678.         diagnose("%s aborted; %s unchanged.",cmdid,RCSfilename);
  679.         status = 1;
  680.         nerror = 0;
  681.         }
  682.         } while (cleanup(),
  683.                  ++argv, --argc >=1);
  684.  
  685.         exit(status);
  686. }       /* end of main (rcs) */
  687.  
  688.  
  689.  
  690. getassoclst(flag, sp)
  691. int     flag;
  692. char    * sp;
  693. /*  Function:   associate a symbolic name to a revision or branch,      */
  694. /*              and store in assoclst                                   */
  695.  
  696. {
  697.         struct   Symrev  * pt;
  698.         char             * temp, *temp2;
  699.         int                c;
  700.  
  701.         while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
  702.         temp = sp;
  703.         temp2=checkid(sp, ':');  /*  check for invalid symbolic name  */
  704.         sp = temp2; c = *sp;   *sp = '\0';
  705.         while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
  706.  
  707.         if ( c != ':' && c != '\0') {
  708.         error("Invalid string %s after option -n or -N",sp);
  709.             return;
  710.         }
  711.  
  712.         pt = (struct Symrev *)talloc(sizeof(struct Symrev));
  713.         pt->ssymbol = temp;
  714.         pt->override = flag;
  715.     if (c == '\0')  /*  delete symbol  */
  716.             pt->revno = nil;
  717.         else {
  718.             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
  719.         if ( c == '\0' )
  720.                 pt->revno = nil;
  721.         else
  722.                 pt->revno = sp;
  723.         }
  724.         pt->nextsym = nil;
  725.         if (lastassoc)
  726.             lastassoc->nextsym = pt;
  727.         else
  728.             assoclst = pt;
  729.         lastassoc = pt;
  730.         return;
  731. }
  732.  
  733.  
  734.  
  735. struct access * getaccessor( sp)
  736. char            *sp;
  737. /*   Function:  get the accessor list of options -e and -a,     */
  738. /*              and store in curpt                              */
  739.  
  740.  
  741. {
  742.         struct  access  * curpt, * pt,  *pre;
  743.         char    *temp;
  744.         register c;
  745.  
  746.         while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
  747.         if ( c == '\0') {
  748.             error("Missing login name after option -a or -e");
  749.             return nil;
  750.         }
  751.  
  752.         curpt = pt = nil;
  753.         while( c != '\0') {
  754.                 temp=checkid(sp,',');
  755.                 pt = (struct access *)talloc(sizeof(struct access));
  756.                 pt->login = sp;
  757.                 if ( curpt )
  758.                     pre->nextaccess = pt;
  759.                 else
  760.                     curpt = pt;
  761.                 pt->nextaccess = curpt;
  762.                 pre = pt;
  763.                 sp = temp;    c = *sp;   *sp = '\0';
  764.                 while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
  765.         }
  766.         return pt;
  767. }
  768.  
  769.  
  770.  
  771. getstates(sp)
  772. char    *sp;
  773. /*   Function:  get one state attribute and the corresponding   */
  774. /*              revision and store in statelst                  */
  775.  
  776. {
  777.         char    *temp, *temp2;
  778.         struct  Status  *pt;
  779.         register        c;
  780.  
  781.         while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
  782.         temp = sp;
  783.         temp2=checkid(sp,':');  /* check for invalid state attribute */
  784.         sp = temp2;   c = *sp;   *sp = '\0';
  785.         while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
  786.  
  787.         if ( c == '\0' ) {  /*  change state of def. branch or Head  */
  788.             chgheadstate = true;
  789.             headstate  = temp;
  790.             return;
  791.         }
  792.         else if ( c != ':' ) {
  793.             error("Missing ':' after state in option -s");
  794.             return;
  795.         }
  796.  
  797.         while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
  798.         pt = (struct Status *)talloc(sizeof(struct Status));
  799.         pt->status     = temp;
  800.         pt->revno      = sp;
  801.         pt->nextstatus = nil;
  802.         if (laststate)
  803.             laststate->nextstatus = pt;
  804.         else
  805.             statelst = pt;
  806.         laststate = pt;
  807. }
  808.  
  809.  
  810.  
  811. getrplaccess()
  812. /*   Function : get the accesslist of the 'accessfile'  */
  813. /*              and place in rplaccessor                */
  814. {
  815.         register        char    *id, *nextp;
  816.         struct          access  *newaccess, *oldaccess;
  817.  
  818.         if ( (finptr=fopen(accessfile, "r")) == NULL) {
  819.             faterror("Can't open file %s", accessfile);
  820.         }
  821.         Lexinit();
  822.         nextp = &accessorlst[0];
  823.  
  824.         if ( ! getkey(Khead)) faterror("Missing head in %s", accessfile);
  825.         VOID getnum();
  826.         if ( ! getlex(SEMI) ) serror("Missing ';' after head in %s",accessfile);
  827.  
  828.         if (getkey(Kbranch)) { /* optional */
  829.                 Dbranch=getnum();
  830.                 if (!getlex(SEMI)) serror("Missing ';' after branch list");
  831.         }
  832.  
  833.  
  834. #ifdef COMPAT2
  835.         /* read suffix. Only in release 2 format */
  836.         if (getkey(Ksuffix)) {
  837.             if (nexttok==STRING) {
  838.                 readstring(); nextlex(); /*through away the suffix*/
  839.             } elsif(nexttok==ID) {
  840.                 nextlex();
  841.             }
  842.             if ( ! getlex(SEMI) ) serror("Missing ';' after suffix in %s",accessfile);
  843.         }
  844. #endif
  845.  
  846.         if (! getkey(Kaccess))fatserror("Missing access list in %s",accessfile);
  847.         oldaccess = nil;
  848.         while( id =getid() ) {
  849.             newaccess = (struct access *)talloc(sizeof(struct access));
  850.             newaccess->login = nextp;
  851.             newaccess->nextaccess = nil;
  852.             while( ( *nextp++ = *id++) != '\0')  ;
  853.             if ( oldaccess )
  854.                 oldaccess->nextaccess = newaccess;
  855.             else
  856.                 rplaccessor = newaccess;
  857.             oldaccess = newaccess;
  858.         }
  859.         if ( ! getlex(SEMI))serror("Missing ';' after access list in %s",accessfile);
  860.         return;
  861. }
  862.  
  863.  
  864.  
  865. getdelrev(sp)
  866. char    *sp;
  867. /*   Function:  get revision range or branch to be deleted,     */
  868. /*              and place in delrev                             */
  869. {
  870.         int    c;
  871.         struct  delrevpair      *pt;
  872.  
  873.         if (delrev) free((char *)delrev);
  874.  
  875.         pt = (struct delrevpair *)talloc(sizeof(struct delrevpair));
  876.         while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
  877.  
  878.         if ( c == '<' || c == '-' ) {  /*  -o  -rev  or <rev  */
  879.             while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t')  ;
  880.             pt->strt = sp;    pt->code = 1;
  881.             while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
  882.             *sp = '\0';
  883.             pt->end = nil;  delrev = pt;
  884.             return;
  885.         }
  886.         else {
  887.             pt->strt = sp;
  888.             while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
  889.                    && c != '-' && c != '<' )  c = *++sp;
  890.             *sp = '\0';
  891.             while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
  892.             if ( c == '\0' )  {  /*   -o rev or branch   */
  893.                 pt->end = nil;   pt->code = 0;
  894.                 delrev = pt;
  895.                 return;
  896.             }
  897.             if ( c != '-' && c != '<') {
  898.                 faterror("Invalid range %s %s after -o", pt->strt, sp);
  899.                 free((char *)pt);
  900.                 return;
  901.             }
  902.             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
  903.             if ( c == '\0') {  /*  -o   rev-   or   rev<   */
  904.                 pt->end = nil;   pt->code = 2;
  905.                 delrev = pt;
  906.                 return;
  907.             }
  908.         }
  909.         /*   -o   rev1-rev2    or   rev1<rev2   */
  910.         pt->end = sp;  pt->code = 3;   delrev = pt;
  911.         while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
  912.         *sp = '\0';
  913. }
  914.  
  915.  
  916.  
  917.  
  918. scanlogtext(delta,func)
  919. struct hshentry * delta; enum stringwork func;
  920. /* Function: Scans delta text nodes up to and including the one given
  921.  * by delta, or up to last one present, if delta==nil.
  922.  * For the one given by delta (if delta!=nil), the log message is saved into
  923.  * curlogmsg and the text is processed according to parameter func.
  924.  * Assumes the initial lexeme must be read in first.
  925.  * Does not advance nexttok after it is finished, except if delta==nil.
  926.  */
  927. {       struct hshentry * nextdelta;
  928.  
  929.         do {
  930.                 rewriteflag = false;
  931.                 nextlex();
  932.                 if (!(nextdelta=getnum())) {
  933.                     if(delta)
  934.                         faterror("Can't find delta for revision %s", delta->num);
  935.                     else return; /* no more delta text nodes */
  936.                 }
  937.                 if ( nextdelta->selector != DELETE) {
  938.                         rewriteflag = true;
  939.                         VOID fprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
  940.                 }
  941.                 if (!getkey(Klog) || nexttok!=STRING)
  942.                         serror("Missing log entry");
  943.                 elsif (delta==nextdelta) {
  944.                         VOID savestring(curlogmsg,logsize);
  945.                         delta->log=curlogmsg;
  946.                 } else {readstring();
  947.                         if (delta!=nil) delta->log="";
  948.                 }
  949.                 nextlex();
  950.                 if (!getkey(Ktext) || nexttok!=STRING)
  951.                         fatserror("Missing delta text");
  952.  
  953.                 if(delta==nextdelta)
  954.                         /* got the one we're looking for */
  955.                         switch (func) {
  956.                         case copy:      copystring();
  957.                                         break;
  958.                         case edit:      editstring((struct hshentry *)nil);
  959.                                         break;
  960.                         default:        faterror("Wrong scanlogtext");
  961.                         }
  962.                 else    readstring(); /* skip over it */
  963.  
  964.         } while (delta!=nextdelta);
  965. }
  966.  
  967.  
  968.  
  969. releaselst(sourcelst)
  970. struct  access  * sourcelst;
  971. /*   Function:  release the storages whose address are in sourcelst   */
  972.  
  973. {
  974.         struct  access  * pt;
  975.  
  976.         pt = sourcelst;
  977.         while(pt) {
  978.         struct access *pn = pt->nextaccess;
  979.             free((char *)pt);
  980.             pt = pn;
  981.         }
  982. }
  983.  
  984.  
  985.  
  986. struct  Lockrev  * rmnewlocklst(which)
  987. struct  Lockrev  * which;
  988. /*   Function:  remove lock to revision which->revno from newlocklst   */
  989.  
  990. {
  991.         struct  Lockrev   * pt, *pre;
  992.  
  993.         while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
  994.         struct Lockrev *pn = newlocklst->nextrev;
  995.             free((char *)newlocklst);
  996.         newlocklst = pn;
  997.         }
  998.  
  999.         pt = pre = newlocklst;
  1000.         while( pt ) {
  1001.             if ( ! strcmp(pt->revno, which->revno) ) {
  1002.                 pre->nextrev = pt->nextrev;
  1003.                 free((char *)pt);
  1004.         pt = pre->nextrev;
  1005.             }
  1006.             else {
  1007.                 pre = pt;
  1008.                 pt = pt->nextrev;
  1009.             }
  1010.         }
  1011.         return pre;
  1012. }
  1013.  
  1014.  
  1015.  
  1016. struct  access  * removeaccess( who, sourcelst,flag)
  1017. struct  access  * who, * sourcelst;
  1018. int     flag;
  1019. /*   Function:  remove the accessor-- who from sourcelst     */
  1020.  
  1021. {
  1022.         struct  access  *pt, *pre;
  1023.  
  1024.         pt = sourcelst;
  1025.         while( pt && (! strcmp(who->login, pt->login) )) {
  1026.         pre = pt->nextaccess;
  1027.             free((char *)pt);
  1028.         pt = pre;
  1029.             flag = false;
  1030.     }
  1031.         pre = sourcelst = pt;
  1032.         while( pt ) {
  1033.             if ( ! strcmp(who->login, pt->login) ) {
  1034.         pre->nextaccess = pt->nextaccess;
  1035.         free((char *)pt);
  1036.         pt = pre->nextaccess;
  1037.                 flag = false;
  1038.             }
  1039.             else {
  1040.                 pre = pt;
  1041.                 pt = pt->nextaccess;
  1042.             }
  1043.         }
  1044.         if ( flag ) warn("Can't remove a nonexisting accessor %s",who->login);
  1045.         return sourcelst;
  1046. }
  1047.  
  1048.  
  1049.  
  1050. int addnewaccess( who )
  1051. struct  access  * who;
  1052. /*   Function:  add new accessor-- who into AccessList    */
  1053.  
  1054. {
  1055.         struct  access  *pt,  *pre;
  1056.  
  1057.         pre = pt = AccessList;
  1058.  
  1059.         while( pt ) {
  1060.             if ( strcmp( who->login, pt->login) ) {
  1061.                 pre = pt;
  1062.                 pt = pt->nextaccess;
  1063.             }
  1064.             else
  1065.                 return 0;
  1066.         }
  1067.         if ( pre == pt )
  1068.             AccessList = who;
  1069.         else
  1070.             pre->nextaccess = who;
  1071.         return 1;
  1072. }
  1073.  
  1074.  
  1075. sendmail(Delta, who)
  1076. char    * Delta,  *who;
  1077. /*   Function:  mail to who, informing him that his lock on delta was
  1078.  *   broken by caller. Ask first whether to go ahead. Return false on
  1079.  *   error or if user decides not to break the lock.
  1080.  */
  1081. {
  1082.         char    * messagefile;
  1083.         int   old1, old2, c, response;
  1084.         FILE    * mailmess;
  1085.  
  1086.  
  1087.     VOID fprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
  1088.         VOID fprintf(stderr, "Do you want to break the lock? [ny](n): ");
  1089.         response=c=getchar();
  1090.         while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
  1091.         if (response=='\n'||response=='n'||response=='N') return false;
  1092.  
  1093.         /* go ahead with breaking  */
  1094. #ifdef MSDOS
  1095.         messagefile=mktempfile( tmpdir, "mmXXXXXX");
  1096. #else
  1097.         messagefile=mktempfile("/tmp/", "RCSmailXXXXXX");
  1098. #endif /* MSDOS */
  1099.         if ( (mailmess = fopen(messagefile, "w")) == NULL) {
  1100.             faterror("Can't open file %s", messagefile);
  1101.         }
  1102.  
  1103.     VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",bindex(RCSfilename,'/'));
  1104.         VOID fprintf(mailmess, "Your lock on revision %s of file %s\n",Delta, getfullRCSname());
  1105.         VOID fprintf(mailmess,"has been broken by %s for the following reason:\n",caller);
  1106.         VOID fputs("State the reason for breaking the lock:\n", stderr);
  1107.         VOID fputs("(terminate with ^D or single '.')\n>> ", stderr);
  1108.  
  1109.         old1 = '\n';    old2 = ' ';
  1110.         for (; ;) {
  1111.             c = getchar();
  1112.             if ( c == EOF ) {
  1113.                 VOID putc('\n',stderr);
  1114.                 VOID fprintf(mailmess, "%c\n", old1);
  1115.                 break;
  1116.             }
  1117.             else if ( c == '\n' && old1 == '.' && old2 == '\n')
  1118.                 break;
  1119.             else {
  1120.                 VOID fputc( old1, mailmess);
  1121.                 old2 = old1;   old1 = c;
  1122.                 if (c== '\n') VOID fputs(">> ", stderr);
  1123.             }
  1124.         }
  1125.         ffclose(mailmess);
  1126.  
  1127.     /* ignore the exit status, even if delivermail unsuccessful */
  1128.         VOID run(messagefile,(char*)nil,
  1129. #ifdef SENDMAIL
  1130.         "/usr/lib/sendmail",
  1131. #else
  1132. #  ifdef DELIVERMAIL
  1133.         "/etc/delivermail","-w",
  1134. #  else
  1135.         "/bin/mail",
  1136. #  endif
  1137. #endif
  1138.         who,(char*)nil);
  1139.         VOID unlink(messagefile);
  1140.     return(true);
  1141. }
  1142.  
  1143.  
  1144.  
  1145. static breaklock(who,delta)
  1146. char * who; struct hshentry * delta;
  1147. /* function: Finds the lock held by who on delta,
  1148.  * and removes it.
  1149.  * Sends mail if a lock different from the caller's is broken.
  1150.  * Prints an error message if there is no such lock or error.
  1151.  */
  1152. {
  1153.         register struct lock * next, * trail;
  1154.         char * num;
  1155.         struct lock dummy;
  1156.         int whor, numr;
  1157.  
  1158.     num=delta->num;
  1159.         dummy.nextlock=next=Locks;
  1160.         trail = &dummy;
  1161.         while (next!=nil) {
  1162.         if (num != nil)
  1163.             numr = strcmp(num, next->delta->num);
  1164.  
  1165.         whor=strcmp(who,next->login);
  1166.         if (whor==0 && numr==0) break; /* exact match */
  1167.         if (numr==0 && whor !=0) {
  1168.                         if (!sendmail( num, next->login)){
  1169.                             diagnose("%s still locked by %s",num,next->login);
  1170.                 return;
  1171.                         } else break; /* continue after loop */
  1172.                 }
  1173.                 trail=next;
  1174.                 next=next->nextlock;
  1175.         }
  1176.         if (next!=nil) {
  1177.                 /*found one */
  1178.                 diagnose("%s unlocked",next->delta->num);
  1179.                 trail->nextlock=next->nextlock;
  1180.                 next->delta->lockedby=nil;
  1181.                 Locks=dummy.nextlock;
  1182.         } else  {
  1183.         error("no lock set on revision %s", num);
  1184.         }
  1185. }
  1186.  
  1187.  
  1188.  
  1189. struct hshentry *searchcutpt(object, length, store)
  1190. char    * object;
  1191. int     length;
  1192. struct  hshentry   * * store;
  1193. /*   Function:  Search store and return entry with number being object. */
  1194. /*              cuttail = nil, if the entry is Head; otherwise, cuttail */
  1195. /*              is the entry point to the one with number being object  */
  1196.  
  1197. {
  1198.         while( compartial( (*store++)->num, object, length)  )  ;
  1199.         store--;
  1200.  
  1201.         if ( *store == Head)
  1202.             cuthead = nil;
  1203.         else
  1204.             cuthead = *(store -1);
  1205.         return *store;
  1206. }
  1207.  
  1208.  
  1209.  
  1210. int  branchpoint(strt, tail)
  1211. struct  hshentry        *strt,  *tail;
  1212. /*   Function: check whether the deltas between strt and tail    */
  1213. /*        are locked or branch point, return 1 if any is  */
  1214. /*        locked or branch point; otherwise, return 0 and */
  1215. /*              mark DELETE on selector                         */
  1216.  
  1217. {
  1218.         struct  hshentry    *pt;
  1219.     struct lock  *lockpt;
  1220.         int     flag;
  1221.  
  1222.  
  1223.         pt = strt;
  1224.         flag = false;
  1225.         while( pt != tail) {
  1226.             if ( pt->branches ){ /*  a branch point  */
  1227.                 flag = true;
  1228.                 error("Can't remove branch point %s", pt->num);
  1229.             }
  1230.         lockpt = Locks;
  1231.         while(lockpt && lockpt->delta != pt)
  1232.         lockpt = lockpt->nextlock;
  1233.         if ( lockpt ) {
  1234.         flag = true;
  1235.         error("Can't remove locked revision %s",pt->num);
  1236.         }
  1237.             pt = pt->next;
  1238.         }
  1239.  
  1240.         if ( ! flag ) {
  1241.             pt = strt;
  1242.             while( pt != tail ) {
  1243.                 pt->selector = DELETE;
  1244.                 diagnose("deleting revision %s ",pt->num);
  1245.                 pt = pt->next;
  1246.             }
  1247.         }
  1248.         return flag;
  1249. }
  1250.  
  1251.  
  1252.  
  1253. removerevs()
  1254. /*   Function:  get the revision range to be removed, and place the     */
  1255. /*              first revision removed in delstrt, the revision before  */
  1256. /*              delstrt in cuthead( nil, if delstrt is head), and the   */
  1257. /*              revision after the last removed revision in cuttail(nil */
  1258. /*              if the last is a leaf                                   */
  1259.  
  1260. {
  1261.         struct  hshentry    *target, *target2, * temp, *searchcutpt();
  1262.         int     length, flag;
  1263.  
  1264.         flag = false;
  1265.         if ( ! expandsym(delrev->strt, &numrev[0]) ) return 0;
  1266.         target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
  1267.         if ( ! target ) return 0;
  1268.         if ( cmpnum(target->num, &numrev[0]) ) flag = true;
  1269.         length = countnumflds( &numrev[0] );
  1270.  
  1271.         if ( delrev->code == 0 ) {  /*  -o  rev    or    -o  branch   */
  1272.         if ( length % 2)
  1273.         temp=searchcutpt(target->num,length+1,gendeltas);
  1274.         else if (flag) {
  1275.                 error("Revision %s does not exist", &numrev[0]);
  1276.         return 0;
  1277.         }
  1278.         else
  1279.         temp = searchcutpt(&numrev[0],length,gendeltas);
  1280.         cuttail = target->next;
  1281.             if ( branchpoint(temp, cuttail) ) {
  1282.                 cuttail = nil;
  1283.                 return 0;
  1284.             }
  1285.             delstrt = temp;     /* first revision to be removed   */
  1286.             return 1;
  1287.         }
  1288.  
  1289.         if ( length % 2 ) {   /*  invalid branch after -o   */
  1290.             error("Invalid branch range %s after -o", &numrev[0]);
  1291.             return 0;
  1292.         }
  1293.  
  1294.         if ( delrev->code == 1 )  {  /*  -o  -rev   */
  1295.             if ( length > 2 ) {
  1296.                 temp = searchcutpt( target->num, length-1, gendeltas);
  1297.                 cuttail = target->next;
  1298.             }
  1299.             else {
  1300.                 temp = searchcutpt(target->num, length, gendeltas);
  1301.                 cuttail = target;
  1302.                 while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
  1303.                     cuttail = cuttail->next;
  1304.             }
  1305.             if ( branchpoint(temp, cuttail) ){
  1306.                 cuttail = nil;
  1307.                 return 0;
  1308.             }
  1309.             delstrt = temp;
  1310.             return 1;
  1311.         }
  1312.  
  1313.         if ( delrev->code == 2 )  {   /*  -o  rev-   */
  1314.             if ( length == 2 ) {
  1315.                 temp = searchcutpt(target->num, 1,gendeltas);
  1316.                 if ( flag)
  1317.                     cuttail = target;
  1318.                 else
  1319.                     cuttail = target->next;
  1320.             }
  1321.             else  {
  1322.                 if ( flag){
  1323.                     cuthead = target;
  1324.                     if ( !(temp = target->next) ) return 0;
  1325.                 }
  1326.                 else
  1327.                     temp = searchcutpt(target->num, length, gendeltas);
  1328.                 getbranchno(temp->num, &numrev[0]);  /*  get branch number  */
  1329.                 target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
  1330.             }
  1331.             if ( branchpoint( temp, cuttail ) ) {
  1332.                 cuttail = nil;
  1333.                 return 0;
  1334.             }
  1335.             delstrt = temp;
  1336.             return 1;
  1337.         }
  1338.  
  1339.         /*   -o   rev1-rev2   */
  1340.         if ( ! expandsym(delrev->end, &numrev[0])  )  return 0;
  1341.         if ( length != countnumflds( &numrev[0] ) ) {
  1342.             error("Invalid revision range %s-%s", target->num, &numrev[0]);
  1343.             return 0;
  1344.         }
  1345.         if ( length > 2 && compartial( &numrev[0], target->num, length-1) ) {
  1346.             error("Invalid revision range %s-%s", target->num, &numrev[0]);
  1347.             return 0;
  1348.         }
  1349.  
  1350.         target2 = genrevs( &numrev[0], (char *)nil, (char *)nil, (char *)nil,gendeltas);
  1351.         if ( ! target2 ) return 0;
  1352.  
  1353.         if ( length > 2) {  /* delete revisions on branches  */
  1354.             if ( cmpnum(target->num, target2->num) > 0) {
  1355.                 if ( cmpnum(target2->num, &numrev[0]) )
  1356.                     flag = true;
  1357.                 else
  1358.                     flag = false;
  1359.                 temp = target;
  1360.                 target = target2;
  1361.                 target2 = temp;
  1362.             }
  1363.             if ( flag ) {
  1364.                 if ( ! cmpnum(target->num, target2->num) ) {
  1365.                     error("Revisions %s-%s don't exist", delrev->strt,delrev->end);
  1366.                     return 0;
  1367.                 }
  1368.                 cuthead = target;
  1369.                 temp = target->next;
  1370.             }
  1371.             else
  1372.                 temp = searchcutpt(target->num, length, gendeltas);
  1373.             cuttail = target2->next;
  1374.         }
  1375.         else { /*  delete revisions on trunk  */
  1376.             if ( cmpnum( target->num, target2->num) < 0 ) {
  1377.                 temp = target;
  1378.                 target = target2;
  1379.                 target2 = temp;
  1380.             }
  1381.             else
  1382.                 if ( cmpnum(target2->num, &numrev[0]) )
  1383.                     flag = true;
  1384.                 else
  1385.                     flag = false;
  1386.             if ( flag ) {
  1387.                 if ( ! cmpnum(target->num, target2->num) ) {
  1388.                     error("Revisions %s-%s don't exist", delrev->strt, delrev->end);
  1389.                     return 0;
  1390.                 }
  1391.                 cuttail = target2;
  1392.             }
  1393.             else
  1394.                 cuttail = target2->next;
  1395.             temp = searchcutpt(target->num, length, gendeltas);
  1396.         }
  1397.         if ( branchpoint(temp, cuttail) )  {
  1398.             cuttail = nil;
  1399.             return 0;
  1400.         }
  1401.         delstrt = temp;
  1402.         return 1;
  1403. }
  1404.  
  1405.  
  1406.  
  1407. updateassoc()
  1408. /*   Function: add or delete(if revno is nil) association    */
  1409. /*        which is stored in assoclst            */
  1410.  
  1411. {
  1412.         struct  Symrev  * curassoc;
  1413.     struct  assoc   * pre,  * pt;
  1414.         struct  hshentry    * target;
  1415.  
  1416.         /*  add new associations   */
  1417.         curassoc = assoclst;
  1418.         while( curassoc ) {
  1419.             if ( curassoc->revno == nil ) {  /* delete symbol  */
  1420.         pre = pt = Symbols;
  1421.                 while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
  1422.             pre = pt;
  1423.             pt = pt->nextassoc;
  1424.         }
  1425.         if ( pt )
  1426.             if ( pre == pt )
  1427.             Symbols = pt->nextassoc;
  1428.             else
  1429.             pre->nextassoc = pt->nextassoc;
  1430.         else
  1431.                     warn("Can't delete nonexisting symbol %s",curassoc->ssymbol);
  1432.         }
  1433.             else if ( expandsym( curassoc->revno, &numrev[0] ) ) {
  1434.         /*   add symbol  */
  1435.                target = (struct hshentry *) talloc(sizeof(struct hshentry));
  1436.                target->num = &numrev[0];
  1437.                VOID addsymbol(target, curassoc->ssymbol, curassoc->override);
  1438.             }
  1439.             curassoc = curassoc->nextsym;
  1440.         }
  1441.  
  1442. }
  1443.  
  1444.  
  1445.  
  1446. updatelocks()
  1447. /* Function: remove lock for caller or first lock if unlockcaller==true;
  1448.  *           remove locks which are stored in rmvlocklst,
  1449.  *           add new locks which are stored in newlocklst,
  1450.  *           add lock for Dbranch or Head if lockhead==true.
  1451.  */
  1452. {
  1453.         struct  hshentry        *target;
  1454.         struct  Lockrev         *lockpt;
  1455.  
  1456.         if(unlockcaller == true) { /*  find lock for caller  */
  1457.             if ( Head ) {
  1458.         if (Locks) {
  1459.             target=findlock(caller,true);
  1460.             if (target==nil) {
  1461.             breaklock(caller, Locks->delta); /* remove most recent lock */
  1462.             } else {
  1463.             diagnose("%s unlocked",target->num);
  1464.             }
  1465.         } else {
  1466.             warn("There are no locks set.");
  1467.         }
  1468.             } else {
  1469.                 warn("Can't unlock an empty tree");
  1470.             }
  1471.         }
  1472.  
  1473.         /*  remove locks which are stored in rmvlocklst   */
  1474.         lockpt = rmvlocklst;
  1475.         while( lockpt ) {
  1476.         if (expandsym(lockpt->revno, numrev)) {
  1477.         target = genrevs(numrev, (char *)nil, (char *)nil, (char *)nil, gendeltas);
  1478.                 if ( target )
  1479.            if ( !(countnumflds(numrev)%2) && cmpnum(target->num,numrev))
  1480.             error("Can't unlock nonexisting revision %s",lockpt->revno);
  1481.                    else
  1482.                         breaklock(caller, target);
  1483.                         /* breaklock does its own diagnose */
  1484.             }
  1485.             lockpt = lockpt->nextrev;
  1486.         }
  1487.  
  1488.         /*  add new locks which stored in newlocklst  */
  1489.         lockpt = newlocklst;
  1490.         while( lockpt ) {
  1491.             setlock(lockpt->revno,caller);
  1492.             lockpt = lockpt->nextrev;
  1493.         }
  1494.  
  1495.         if ( lockhead == true) {  /*  lock default branch or head  */
  1496.             if (Dbranch) {
  1497.                 setlock(Dbranch->num,caller);
  1498.             } elsif ( Head) {
  1499.                 if (addlock(Head, caller))
  1500.                     diagnose("%s locked",Head->num);
  1501.             } else {
  1502.                 warn("Can't lock an empty tree");
  1503.             }
  1504.         }
  1505.  
  1506. }
  1507.  
  1508.  
  1509.  
  1510. setlock(rev,who)
  1511. char * rev, * who;
  1512. /* Function: Given a revision or branch number, finds the correponding
  1513.  * delta and locks it for who.
  1514.  */
  1515. {
  1516.         struct  lock     *lpt;
  1517.         struct  hshentry *target;
  1518.  
  1519.         if (expandsym(rev, &numrev[0]) ){
  1520.             target = genrevs(&numrev[0],(char *) nil,(char *) nil,
  1521.                  (char *)nil, gendeltas);
  1522.             if ( target )
  1523.                if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num,&numrev[0]))
  1524.                     error("Can't lock nonexisting revision %s",numrev);
  1525.                else
  1526.                     if(lpt=addlock(target, who))
  1527.                         diagnose("%s locked",lpt->delta->num);
  1528.         }
  1529. }
  1530.  
  1531.  
  1532.  
  1533. rcs_setstate(rev,status)
  1534. char * rev, * status;
  1535. /* Function: Given a revision or branch number, finds the corresponding delta
  1536.  * and sets its state to status.
  1537.  */
  1538. {
  1539.         struct  hshentry *target;
  1540.  
  1541.         if ( expandsym(rev, &numrev[0]) ) {
  1542.             target = genrevs(&numrev[0],(char *) nil, (char *)nil,
  1543.                  (char *) nil, gendeltas);
  1544.             if ( target )
  1545.                if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num, &numrev[0]) )
  1546.                     error("Can't set state of nonexisting revision %s to %s",
  1547.                            numrev,status);
  1548.                else
  1549.                     target->state = status;
  1550.         }
  1551. }
  1552.  
  1553.  
  1554.  
  1555.  
  1556.  
  1557. buildeltatext(deltas)
  1558. struct  hshentry        ** deltas;
  1559. /*   Function:  put the delta text on frewrite and make necessary   */
  1560. /*              change to delta text                                */
  1561. {
  1562.         int  i, c, exit_stats;
  1563.  
  1564.         cuttail->selector = DELETE;
  1565. #ifdef MSDOS
  1566.         initeditfiles( tmpdir );
  1567. #else
  1568.         initeditfiles("/tmp/");
  1569. #endif /* MSDOS */
  1570.         scanlogtext(deltas[0], copy);
  1571.         i = 1;
  1572.         if ( cuthead )  {
  1573. #ifdef MSDOS
  1574.             cutfilename=mktempfile( tmpdir, "ctXXXXXX");
  1575. #else
  1576.             cutfilename=mktempfile("/tmp/", "RCScutXXXXXX");
  1577. #endif /* MSDOS */
  1578.             if ( (fcut = fopen(cutfilename, "w")) == NULL) {
  1579.                 faterror("Can't open temporary file %s", cutfilename);
  1580.             }
  1581.  
  1582.             while( deltas[i-1] != cuthead )  {
  1583.                 scanlogtext(deltas[i++], edit);
  1584.             }
  1585.  
  1586.             finishedit((struct hshentry *)nil);    rewind(fcopy);
  1587.             while( (c = getc(fcopy)) != EOF) VOID putc(c, fcut);
  1588.             swapeditfiles(false);
  1589.             ffclose(fcut);
  1590.         }
  1591.  
  1592.         while( deltas[i-1] != cuttail)
  1593.             scanlogtext(deltas[i++], edit);
  1594.         finishedit((struct hshentry *)nil);    ffclose(fcopy);
  1595.  
  1596.         if ( cuthead ) {
  1597. #ifdef MSDOS
  1598.             diffilename=mktempfile( tmpdir, "dfXXXXXX");
  1599. #else
  1600.             diffilename=mktempfile("/tmp/", "RCSdifXXXXXX");
  1601. #endif /* MSDOS */
  1602.             exit_stats = run((char*)nil,diffilename,
  1603. #ifdef MSDOS
  1604. #    ifdef GNUDIFF
  1605.             DIFF,"-n","-a",cutfilename,resultfile,(char*)nil);
  1606. #    else
  1607.             DIFF,"-n",cutfilename,resultfile,(char*)nil);
  1608. #    endif
  1609. #else
  1610.             DIFF,"-n",cutfilename,resultfile,(char*)nil);
  1611. #endif /* MSDOS */
  1612. #ifdef MSDOS
  1613.             if (exit_stats != 0 && exit_stats != 1 )
  1614. #else
  1615.             if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
  1616. #endif /* MSDOS */
  1617.                 faterror ("diff failed");
  1618.             if(!putdtext(cuttail->num,curlogmsg,diffilename,frewrite)) return;
  1619.         }
  1620.         else
  1621.             if (!putdtext(cuttail->num,curlogmsg,resultfile,frewrite)) return;
  1622.  
  1623.         scanlogtext((struct hshentry *)nil,empty); /* read the rest of the deltas */
  1624. }
  1625.  
  1626.  
  1627.  
  1628. buildtree()
  1629. /*   Function:  actually removes revisions whose selector field  */
  1630. /*              is DELETE, and rebuilds  the linkage of deltas.  */
  1631. /*              asks for reconfirmation if deleting last revision*/
  1632. {
  1633.     int c,  response;
  1634.  
  1635.     struct    hshentry   * Delta;
  1636.         struct  branchhead      *pt, *pre;
  1637.  
  1638.         if ( cuthead )
  1639.            if ( cuthead->next == delstrt )
  1640.                 cuthead->next = cuttail;
  1641.            else {
  1642.                 pre = pt = cuthead->branches;
  1643.                 while( pt && pt->hsh != delstrt )  {
  1644.                     pre = pt;
  1645.                     pt = pt->nextbranch;
  1646.                 }
  1647.                 if ( cuttail )
  1648.                     pt->hsh = cuttail;
  1649.                 else if ( pt == pre )
  1650.                     cuthead->branches = pt->nextbranch;
  1651.                 else
  1652.                     pre->nextbranch = pt->nextbranch;
  1653.             }
  1654.     else {
  1655.             if ( cuttail == nil && !quietflag) {
  1656.                 VOID fprintf(stderr,"Do you really want to delete all revisions ?[ny](n): ");
  1657.         c = response = getchar();
  1658.         while( c != EOF && c != '\n') c = getchar();
  1659.                 if ( response != 'y' && response != 'Y') {
  1660.                     diagnose("No revision deleted");
  1661.             Delta = delstrt;
  1662.             while( Delta) {
  1663.             Delta->selector = 'S';
  1664.             Delta = Delta->next;
  1665.             }
  1666.             return;
  1667.         }
  1668.         }
  1669.             Head = cuttail;
  1670.     }
  1671.         return;
  1672. }
  1673.  
  1674.